"""
GitCode API客户端
"""
import os
import logging
import json
from typing import Any, Dict, List, Optional, Union, Tuple, TypeVar, Generic, Type
import aiohttp
from urllib.parse import urlencode

from .errors import (
    GitCodeAPIError,
    GitCodeAuthError,
    GitCodeNotFoundError,
    GitCodePermissionError,
    GitCodeRateLimitError,
    GitCodeServerError,
    GitCodeValidationError,
)

logger = logging.getLogger("gitcode_mcp.api")

T = TypeVar('T')


class BaseAPI:
    """API基类"""
    
    def __init__(self, client: 'GitCodeAPI'):
        self._client = client
    
    @property
    def api_base_url(self) -> str:
        """API基础URL"""
        return self._client.api_base_url
    
    async def request(self, *args, **kwargs) -> Any:
        """执行API请求"""
        return await self._client.request(*args, **kwargs)


class GitCodeAPI:
    """GitCode API客户端"""
    
    def __init__(
        self, 
        token: Optional[str] = None, 
        api_base_url: str = "https://api.gitcode.com/api/v5",
        timeout: float = 30.0
    ):
        """
        初始化GitCode API客户端
        
        Args:
            token: GitCode个人访问令牌，如果为None则尝试从环境变量读取
            api_base_url: API基础URL
            timeout: 请求超时时间（秒）
        """
        # 懒加载API子模块
        self._repos = None
        self._branches = None
        self._issues = None
        self._pulls = None
        self._search = None
        
        self.token = token or os.environ.get("GITCODE_TOKEN")
        if not self.token:
            raise ValueError("GitCode令牌未提供。请设置GITCODE_TOKEN环境变量或在初始化时提供token参数")
        
        self.api_base_url = api_base_url
        self.timeout = timeout
        
        # 用于API请求的会话
        self._session: Optional[aiohttp.ClientSession] = None
    
    @property
    def repos(self):
        """仓库API"""
        if self._repos is None:
            from ..repos import RepositoryAPI
            self._repos = RepositoryAPI(self)
        return self._repos
    
    @property
    def branches(self):
        """分支API"""
        if self._branches is None:
            from ..branches import BranchAPI
            self._branches = BranchAPI(self)
        return self._branches
    
    @property
    def issues(self):
        """Issue API"""
        if self._issues is None:
            from ..issues import IssueAPI
            self._issues = IssueAPI(self)
        return self._issues
    
    @property
    def pulls(self):
        """Pull Request API"""
        if self._pulls is None:
            from ..pulls import PullRequestAPI
            self._pulls = PullRequestAPI(self)
        return self._pulls
    
    @property
    def search(self):
        """搜索API"""
        if self._search is None:
            from ..search import SearchAPI
            self._search = SearchAPI(self)
        return self._search
    
    async def ensure_session(self) -> aiohttp.ClientSession:
        """确保API会话已创建并返回"""
        if self._session is None or self._session.closed:
            self._session = aiohttp.ClientSession(
                headers={
                    "Authorization": f"Bearer {self.token}",
                    "Content-Type": "application/json",
                    "Accept": "application/json",
                    "User-Agent": "GitCode-MCP-Client/1.0.0",
                },
                timeout=aiohttp.ClientTimeout(total=self.timeout)
            )
            logger.debug("创建新的API会话")
        return self._session
    
    async def close(self):
        """关闭API会话"""
        if self._session and not self._session.closed:
            logger.debug("关闭API会话")
            await self._session.close()
            self._session = None
    
    def _build_url(self, path: str, params: Optional[Dict[str, Any]] = None) -> str:
        """构建完整URL"""
        url = f"{self.api_base_url}{path}"
        
        if params:
            # 过滤掉None值
            filtered_params = {k: v for k, v in params.items() if v is not None}
            if filtered_params:
                query_string = urlencode(filtered_params)
                url = f"{url}?{query_string}"
        
        return url
    
    async def _handle_response(self, response: aiohttp.ClientResponse) -> Any:
        """处理API响应"""
        status_code = response.status
        
        if 200 <= status_code < 300:
            if status_code == 204:  # No Content
                return None
            
            try:
                return await response.json()
            except json.JSONDecodeError:
                text = await response.text()
                logger.error(f"解析JSON响应失败: {text}")
                raise GitCodeAPIError(status_code, f"解析响应失败: {text}")
        
        # 处理错误响应
        try:
            error_json = await response.json()
            error_message = error_json.get("message", "未知错误")
        except (json.JSONDecodeError, KeyError):
            error_message = await response.text() or "未知错误"
        
        # 根据状态码选择异常类型
        if status_code == 401:
            raise GitCodeAuthError(error_message)
        elif status_code == 403:
            raise GitCodePermissionError(error_message)
        elif status_code == 404:
            raise GitCodeNotFoundError("资源", error_message)
        elif status_code == 422:
            raise GitCodeValidationError(error_message)
        elif status_code == 429:
            raise GitCodeRateLimitError(error_message)
        elif status_code >= 500:
            raise GitCodeServerError(status_code, error_message)
        else:
            raise GitCodeAPIError(status_code, error_message)
    
    async def request(
        self, 
        method: str, 
        path: str, 
        params: Optional[Dict[str, Any]] = None,
        data: Any = None,
        headers: Optional[Dict[str, str]] = None,
        **kwargs
    ) -> Any:
        """
        发送API请求
        
        Args:
            method: HTTP方法 (GET, POST, PUT, DELETE等)
            path: API路径 (不包括基础URL)
            params: URL查询参数
            data: 请求数据，会自动转换为JSON
            headers: 额外的请求头
            **kwargs: 传递给aiohttp请求方法的参数
            
        Returns:
            解析后的JSON响应
            
        Raises:
            GitCodeAPIError: API返回错误状态码时
            aiohttp.ClientError: 请求失败时
        """
        session = await self.ensure_session()
        url = self._build_url(path, params)
        
        request_kwargs = kwargs.copy()
        
        # 添加额外的请求头
        if headers:
            request_kwargs["headers"] = headers
        
        # 处理请求数据
        if data is not None:
            if isinstance(data, dict) or isinstance(data, list):
                request_kwargs["json"] = data
            else:
                request_kwargs["data"] = data
        
        # 记录请求信息
        logger.debug(f"API请求: {method} {url}")
        
        try:
            async with session.request(method, url, **request_kwargs) as response:
                return await self._handle_response(response)
        except aiohttp.ClientError as e:
            logger.error(f"HTTP请求失败: {e}")
            raise
    
    async def get(self, path: str, **kwargs) -> Any:
        """发送GET请求"""
        return await self.request("GET", path, **kwargs)
    
    async def post(self, path: str, **kwargs) -> Any:
        """发送POST请求"""
        return await self.request("POST", path, **kwargs)
    
    async def put(self, path: str, **kwargs) -> Any:
        """发送PUT请求"""
        return await self.request("PUT", path, **kwargs)
    
    async def delete(self, path: str, **kwargs) -> Any:
        """发送DELETE请求"""
        return await self.request("DELETE", path, **kwargs)
    
    async def patch(self, path: str, **kwargs) -> Any:
        """发送PATCH请求"""
        return await self.request("PATCH", path, **kwargs)
    
    async def head(self, path: str, **kwargs) -> Any:
        """发送HEAD请求"""
        return await self.request("HEAD", path, **kwargs)
    
    async def options(self, path: str, **kwargs) -> Any:
        """发送OPTIONS请求"""
        return await self.request("OPTIONS", path, **kwargs) 